#region Imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endregion

namespace DataAnnotationsContrib.Tests.Framework
{
    /// <summary>
    /// </summary>
    public static class TestHelper
    {
        #region Constants and Enums
        #endregion

        #region Inner Classes and Structures
        #endregion

        #region Delegates and Events
        public delegate void TestOperation();
        private delegate void ExecuteTestOperationDelegate(TestOperation test, List<Exception> errors);
        #endregion

        #region Instance and Shared Fields
        #endregion

        #region Constructors
        #endregion

        #region Properties
        #endregion

        #region Private and Protected Methods
        #endregion

        #region Public Methods
        public static void Trace(string text)
        {
            Console.WriteLine(text);
        }
        public static void Trace(string format, params object[] args)
        {
            Console.WriteLine(format, args);
        }
        public static void Trace(IEnumerable items)
        {
            Trace(items, Int32.MaxValue);
        }
        public static void Trace(IEnumerable items, int maximumNumberOfItems)
        {
            int counter = 0;
            foreach (object loopItem in items)
            {
                counter++;
                Console.WriteLine("Item {0}: '{1}'", counter - 1, loopItem);
                if (counter == maximumNumberOfItems) break;
            }
        }
        public static void Trace(TestContext context, string text)
        {
            context.WriteLine(text);
        }
        public static void Trace(TestContext context, string format, params object[] args)
        {
            context.WriteLine(format, args);
        }
        public static void Trace(TestContext context, IEnumerable items)
        {
            Trace(context, items, Int32.MaxValue);
        }
        public static void Trace(TestContext context, IEnumerable items, int maximumNumberOfItems)
        {
            int counter = 0;
            foreach (object loopItem in items)
            {
                counter++;
                context.WriteLine("Item {0}: '{1}'", counter - 1, loopItem);
                if (counter == maximumNumberOfItems) break;
            }
        }

        public static TimeSpan Time(TestOperation operation)
        {
            //Delegate to richer overload.
            return Time(operation, 1);
        }
        public static TimeSpan Time(TestOperation operation, int repeatCount)
        {
            int attempts = repeatCount;
            DateTime start = DateTime.Now;
            while (attempts > 0)
            {
                operation.DynamicInvoke(null);
                attempts--;
            }
            DateTime end = DateTime.Now;

            return new TimeSpan((long)Math.Floor((double)(end - start).Ticks / repeatCount));
        }

        public static void RunConcurrently(TestOperation operation, int concurrentExecutionCount)
        {
            List<TestOperation> operations = new List<TestOperation>();
            for (int i = 0; i < concurrentExecutionCount; i++)
            {
                operations.Add(new TestOperation(operation));
            }
            //Delegate to overload.
            RunConcurrently(operations.ToArray());
        }
        public static void RunConcurrently(TestOperation[] operations)
        {
            List<IAsyncResult> results = new List<IAsyncResult>();
            List<Exception> errors = new List<Exception>();

            //Essentially a closure wrapping a test delegate (and capturing the local errors collection).
            Action<TestOperation> operationRunner = delegate(TestOperation o)
            {
                try { o.Invoke(); }
                catch (Exception ex)
                { lock (errors) errors.Add(ex); }
            };

            foreach (TestOperation test in operations)
            {
                IAsyncResult invoke = operationRunner.BeginInvoke(test, null, null);
                results.Add(invoke);
            }

            //Clear up resource by calling EndInvoke() on all delegates.
            foreach (IAsyncResult result in results)
            {
                result.AsyncWaitHandle.WaitOne();
                operationRunner.EndInvoke(result);
            }

            //Pass/Fail.
            if (errors.Count > 0)
            {
                foreach (Exception error in errors)
                {
                    Trace(error.ToString());
                }
                Assert.Fail("One or more exceptions were thrown from simultaneous execution, see the Console.Out for details.");
            }
        }

        public static string GetTestDataPath(TestContext fromContext, string directoryName)
        {
            return Path.Combine(fromContext.TestDeploymentDir, directoryName);
        }

        public static string GetTestDataFile(TestContext fromContext, string directoryName, string fileName)
        {
            return Path.Combine(GetTestDataPath(fromContext, directoryName), fileName);
        }
        public static string[] GetAllTestDataFiles(TestContext fromContext, string directoryName, string filePattern)
        {
            return Directory.GetFiles(GetTestDataPath(fromContext, directoryName), filePattern);
        }
        #endregion

        #region Event Handlers
        #endregion

        #region Base Class Overrides
        #endregion
    }
}